#include <QtGui>
#include <cmath>

#include "plotter.h"


Plotter::Plotter(QVector<Bar>*  pbars, QWidget *parent): QWidget(parent)
{
	setBackgroundRole(QPalette::Dark);
	setAutoFillBackground(true);
	setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
	setFocusPolicy(Qt::StrongFocus);


	m_parent = parent;


	//Bars
	m_pBars = pbars;

	//cursorshown
	m_bCursorShown = false;

	m_rect.setRect(Margin, Margin,	width() - 2 * Margin, height() - 2 * Margin);

	//Init m_zoomset and m_zoomIndex
	{
		double factor = 0.8;

		m_zoomset[0].distance = 5;
		m_zoomset[0].barwidth = m_zoomset[0].distance * factor;
		m_zoomset[1].distance = 10;
		m_zoomset[1].barwidth = m_zoomset[1].distance * factor;
		m_zoomset[2].distance = 20;
		m_zoomset[2].barwidth = m_zoomset[2].distance * factor;
		m_zoomset[3].distance = 30;
		m_zoomset[3].barwidth = m_zoomset[3].distance * factor;
		m_zoomset[4].distance = 40;
		m_zoomset[4].barwidth = m_zoomset[4].distance * factor;
		m_zoomset[5].distance = 50;
		m_zoomset[5].barwidth = m_zoomset[5].distance * factor;

		m_zoomIndex = 2;
	}


	//init Business Cooridinate
	{
		m_pBusiCoord = new BusiCoord(m_rect, m_pBars, m_zoomset[m_zoomIndex]);
	}

	
	

	//Draw elements on buffers

	RefreshBuffer();

}




void Plotter::RefreshBuffer()
{
	pixmap = QPixmap(size());
	pixmap.fill(this, 0, 0);

	QPainter painter(&pixmap);
	painter.initFrom(this);
	bool ret1 = DrawGrid(&painter);
	bool ret2 = DrawBars(&painter);
	if (ret1 && ret2)
		update();
	

}

bool Plotter::DrawGrid(QPainter *painter)
{
		
	int minXIndex; int maxXIndex; double minY; double maxY; int numYGridArea;
	if (false == m_pBusiCoord->GetBusiCoord(minXIndex, maxXIndex, minY, maxY, numYGridArea))
		return false;
	

	QPen quiteDark = palette().dark().color().light();
	QPen light = palette().light().color();


	int shift = m_zoomset[m_zoomIndex].barwidth / 2;
	int distance = m_zoomset[m_zoomIndex].distance;

	
	int num = 0;

	for (int index = minXIndex; index <= maxXIndex; ++index, num++)
	{
		//if (0 == num % 5)
		{
			int x = m_rect.left() + shift + distance * num;

			QString strlable =  QString::number(m_pBars->at(index).index);


			painter->setPen(light);
			painter->drawLine(x, m_rect.bottom(), x, m_rect.bottom() + 5);
			painter->drawText(x - Margin, m_rect.bottom() + 5, 100, 20,
				Qt::AlignHCenter | Qt::AlignTop,
				strlable);
		}
		
		
	}

	


	for (int j = 0; j <= numYGridArea; ++j) 
	{
		int y = m_rect.bottom() - (j * (m_rect.height() - 1)	/ numYGridArea);
		double label = minY + (j * (maxY - minY) / numYGridArea);


		painter->setPen(quiteDark);
		painter->drawLine(m_rect.left(), y, m_rect.right(), y);
		painter->setPen(light);
		painter->drawLine(m_rect.left() - 5, y, m_rect.left(), y);
		painter->drawText(m_rect.left() - Margin, y - 10, Margin - 5, 20,
			Qt::AlignRight | Qt::AlignVCenter,
			QString::number(label));
	}


	painter->setPen(quiteDark);

	painter->drawRect(m_rect.adjusted(0, 0, -1, -1));

	return true;
}

bool Plotter::DrawBars(QPainter *painter)
{
	int minXIndex; int maxXIndex; double minY; double maxY; int numYGridArea;
	if (false == m_pBusiCoord->GetBusiCoord(minXIndex, maxXIndex, minY, maxY, numYGridArea))
		return false;



	int shift = m_zoomset[m_zoomIndex].barwidth / 2;
	int distance = m_zoomset[m_zoomIndex].distance;
	int barwidth = m_zoomset[m_zoomIndex].barwidth;

	




	int num = 0;

	for (int index = minXIndex; index <= maxXIndex; ++index, num++)
	{
		//if (0 == num % 5)
		{
			int x = m_rect.left() + distance * num;


			double dy = (maxY - minY) / (m_rect.height() - 1);


			int y = m_rect.bottom() - (m_pBars->at(index).open - minY) / dy;


			int width = barwidth - 1;

			int height =  -(m_pBars->at(index).close - m_pBars->at(index).open) / dy;
			

			if (height < 0)
			{
				painter->setPen(QPen(Qt::red));
				painter->setBrush(QBrush(Qt::red, Qt::SolidPattern));
			}
			else
			{
				painter->setPen(QPen(Qt::green));
				painter->setBrush(QBrush(Qt::green, Qt::SolidPattern));
			}
				
			

			painter->drawRect(x, y, width, height);




			int line_x1 = x + shift;
			int line_x2 = x + shift;

			int line_y1 = m_rect.bottom() - (m_pBars->at(index).high - minY) / dy;
			int line_y2 = m_rect.bottom() - (m_pBars->at(index).low - minY) / dy;

			painter->drawLine(line_x1, line_y1, line_x2, line_y2);


		
		}


	}

	return true;
}

//Scroll won't cause not shown bar like resizeORzoom
//
void Plotter::Scroll(int dx)
{
	if (m_pBusiCoord->Scroll(dx))
	{
		RefreshBuffer();
	}
}



void Plotter::Zoom(zoomset option)
{

	bool bupdate = false;

	if (zoomin == option)
	{
		if (m_zoomIndex < sizeof(m_zoomset)/sizeof(ZoomSeetings) - 1)
		{
			m_zoomIndex++;

			bupdate = true;
		}
		
	}
	else if (zoomout == option)
	{
		if (m_zoomIndex > 0)
		{
			m_zoomIndex--;

			bupdate = true;
		}
	}


	if (bupdate)
	{
		resizeORzoom();
	}
	



}




void Plotter::paintEvent(QPaintEvent * /* event */)
{
	QStylePainter painter(this);



	painter.drawPixmap(0, 0, pixmap);

	
	

	if (m_bCursorShown) 
	{
		QPen quiteDark = palette().dark().color().light();
		painter.setPen(quiteDark);



		painter.drawLine(m_rect.left(), m_cursorPoint.y(), m_rect.left() + m_rect.width(), m_cursorPoint.y());
		painter.drawLine(m_cursorPoint.x(), m_rect.top(), m_cursorPoint.x(), m_rect.top() + m_rect.height());

		
	}



}

void Plotter::resizeEvent(QResizeEvent * /* event */)
{
	resizeORzoom();
}



void Plotter::mousePressEvent(QMouseEvent *event)
{

    if (event->button() == Qt::LeftButton) 
	{
        if (m_rect.contains(event->pos())) 
		{
			m_bCursorShown = true;
			
			

			m_cursorPoint.setX(event->pos().x());
			m_cursorPoint.setY(event->pos().y());

			//printf("[mousePressEvent] x:%d, y:%d\n", m_cursorPoint.x(), m_cursorPoint.y());

			
			update(m_rect.left(), m_cursorPoint.y(), m_rect.width(), 1);
			update(m_cursorPoint.x(), m_rect.top(), 1, m_rect.height());
			

			
			setCursor(Qt::CrossCursor);
        }


    }
}



void Plotter::mouseMoveEvent(QMouseEvent *event)
{
	
    if (m_rect.contains(event->pos()) && m_bCursorShown) 
	{
		
		//printf("[mouseMoveEvent] b4 reset\n");
		update(m_rect.left(), m_cursorPoint.y(), m_rect.width(), 1);
		update(m_cursorPoint.x(), m_rect.top(), 1, m_rect.height());


		m_cursorPoint.setX(event->pos().x());
		m_cursorPoint.setY(event->pos().y());


		int distance = m_zoomset[m_zoomIndex].distance;
		int curindex = (m_cursorPoint.x() - m_rect.left())/distance;

		//m_parent->SetViewWidget(curindex, m_pBars[curindex].high, m_pBars[curindex].low);





		//printf("[mouseMoveEvent] after reset\n");
		update(m_rect.left(), m_cursorPoint.y(), m_rect.width(), 1);
		update(m_cursorPoint.x(), m_rect.top(), 1, m_rect.height());


    }
}



void Plotter::mouseReleaseEvent(QMouseEvent *event)
{
    if ((event->button() == Qt::LeftButton) && m_bCursorShown) 
	{
        m_bCursorShown = false;

		
		//printf("[mouseReleaseEvent] b4 reset\n");
		update(m_rect.left(), m_cursorPoint.y(), m_rect.width(), 1);
		update(m_cursorPoint.x(), m_rect.top(), 1, m_rect.height());
		
		unsetCursor();
    }
}

void Plotter::keyPressEvent(QKeyEvent *event)
{
    switch (event->key()) 
	{
		case Qt::Key_Left:

			Scroll(-1);
			break;

		case Qt::Key_Right:
			
			Scroll(1);
			break;

		case Qt::Key_Down:

			Zoom(zoomout);
			break;

		case Qt::Key_Up:

			Zoom(zoomin);
			break;


		default:
			QWidget::keyPressEvent(event);
    }
}

//resize Or zoom operation may cause not shown bar(invalid m_rect or bigger bar size)
//so set last element to display (set -1 pamameter) 
void Plotter::resizeORzoom()
{
	int destIndex = -1;

	//for not m_rect not big enough set maxXIndex to size() - 1;
	int minXIndex; int maxXIndex; double minY; double maxY; int numYGridArea;
	if (m_pBusiCoord->GetBusiCoord(minXIndex, maxXIndex, minY, maxY, numYGridArea))
		destIndex = maxXIndex;

	m_rect.setRect(Margin, Margin,	width() - 2 * Margin, height() - 2 * Margin);

	m_pBusiCoord->ReInit(m_rect, m_zoomset[m_zoomIndex], destIndex);


	RefreshBuffer();
}


QDockWidget* Plotter::CreateBardockWidget()
{
	indexLabel = new QLabel(tr("Index:"));
	indexLineEdit = new QLineEdit;
	indexLineEdit->setEnabled(false);
	timeLabel = new QLabel(tr("time:"));
	timeLineEdit = new QLineEdit;
	timeLineEdit->setEnabled(false);
	openLabel = new QLabel(tr("open:"));
	openLineEdit = new QLineEdit;
	openLineEdit->setEnabled(false);
	highLabel = new QLabel(tr("high:"));
	highLineEdit = new QLineEdit;
	highLineEdit->setEnabled(false);
	lowLabel = new QLabel(tr("low:"));
	lowLineEdit = new QLineEdit;
	lowLineEdit->setEnabled(false);
	closeLabel = new QLabel(tr("close:"));
	closeLineEdit = new QLineEdit;
	closeLineEdit->setEnabled(false);
	spacerItem = new QSpacerItem(QSizePolicy::Minimum, QSizePolicy::ExpandFlag);

	QGridLayout *barLayout = new QGridLayout;
	barLayout->addWidget(indexLabel, 0, 0);
	barLayout->addWidget(indexLineEdit, 0, 1);
	barLayout->addWidget(timeLabel, 0, 2);
	barLayout->addWidget(timeLineEdit, 0, 3);
	barLayout->addWidget(openLabel, 1, 0);
	barLayout->addWidget(openLineEdit, 1, 1);
	barLayout->addWidget(highLabel, 1, 2);
	barLayout->addWidget(highLineEdit, 1, 3);
	barLayout->addWidget(lowLabel, 2, 0);
	barLayout->addWidget(lowLineEdit, 2, 1);
	barLayout->addWidget(closeLabel, 2, 2);
	barLayout->addWidget(closeLineEdit, 2, 3);
	barLayout->addItem(spacerItem, 3, 0, 4, 1);


	barWidget = new QWidget;
	barWidget->setLayout(barLayout);


	bardockWidget = new QDockWidget(tr("BarInfo"));
	bardockWidget->setObjectName("bardockWidget_objectname");
	bardockWidget->setWidget(barWidget);


	return bardockWidget;

}

BusiCoord::BusiCoord(QRect& rect, QVector<Bar>* pbars, ZoomSeetings& zoomset)
{
	m_pBars = pbars;



	m_bValid = false;

	if (SetBusiCoord(rect, zoomset, -1))
		m_bValid = true;
	

	
}

void BusiCoord::ReInit(QRect& m_rect, ZoomSeetings& zoomset, int paraMaxIndex)
{

	m_bValid = false;

	if (SetBusiCoord(m_rect, zoomset, paraMaxIndex))
		m_bValid = true;
}

bool BusiCoord::SetBusiCoord(QRect& rect, ZoomSeetings& zoomset, int paraMaxIndex)
{
	//Get contain num in m_rect
	int num = GetContainBarNum(rect, zoomset);
	if (0 == num || 0 == m_pBars->size())
		return false;


	{
		//set m_minXIndex = m_maxXIndex
		if (-1 == paraMaxIndex)
			m_maxXIndex = m_pBars->size() - 1;
		else
			m_maxXIndex = paraMaxIndex;
		
		m_minXIndex = m_maxXIndex - num + 1;

		if (m_minXIndex < 0)
		{
			int tmp = -m_minXIndex;

			m_maxXIndex += tmp;

			if (m_maxXIndex > m_pBars->size() - 1)
			{
				m_maxXIndex = m_pBars->size() - 1;
			}
			

			m_minXIndex = 0;
		}

		//printf("[SetBusiCoord] m_minXIndex:%d, m_maxXIndex:%d\n", m_minXIndex, m_maxXIndex);
	}


	
	{
		//set m_minY = m_maxY

		m_minY = m_pBars->at(m_minXIndex).low;
		m_maxY = m_pBars->at(m_minXIndex).high;

		for (int i = m_minXIndex; i <= m_maxXIndex; i++)
		{
			double low = m_pBars->at(i).low;
			double high = m_pBars->at(i).high;

			if (low < m_minY)
			{
				m_minY = low;
			}

			if (high > m_maxY)
			{
				m_maxY = high;
			}

		}
	}


	

	m_numYGridArea = adjustAxis(m_minY, m_maxY);

	return true;
}


int BusiCoord::GetContainBarNum(QRect& rect, ZoomSeetings& zoomset)
{

	int num = 0;


	num = rect.width() / zoomset.distance - 2;

	if (num < 0)
	{
		num = 0;
	}

	//printf("[GetContainBarNum] width:%d, heigth:%d, num:%d  ", m_rect.width(), m_rect.height(), num);

	return num;

}


bool BusiCoord::Scroll(int dx)
{
	if (m_bValid)
	{
		if (dx < 0 && m_minXIndex > 0)
		{
			m_minXIndex += dx;
			m_maxXIndex += dx;

			return true;
		}
		else if (dx > 0 && m_maxXIndex < m_pBars->size() - 1)
		{
			m_minXIndex += dx;
			m_maxXIndex += dx;

			return true;
		}

	}
	
	return false;

}



int BusiCoord::adjustAxis(double min, double max)
{
	//const int MinTicks = 4;
	//double grossStep = (max - min) / MinTicks;
	//double step = std::pow(10.0, std::floor(std::log10(grossStep)));

	//if (5 * step < grossStep) {
	//	step *= 5;
	//} else if (2 * step < grossStep) {
	//	step *= 2;
	//}

	//int numGridArea = int((max / step) - (min / step));
	//if (numGridArea < MinTicks)
	//	numGridArea = MinTicks;

	//return numGridArea;

	return 4;
}


bool BusiCoord::GetBusiCoord(int& minXIndex, int& maxXIndex, double& minY, double& maxY, int& numYGridArea)
{
	if (m_bValid)
	{
		minXIndex = m_minXIndex;

		maxXIndex = m_maxXIndex;

		minY = m_minY;

		maxY = m_maxY;

		numYGridArea = m_numYGridArea;

		return true;
	}

	return false;

}